#ifndef IOREGISTER_HPP
#define IOREGISTER_HPP

#include <QMutexLocker>

#include "register.hpp"
#include "hardwareinterface.hpp"

class HardwareInterface;

/**
* @brief
*
* @class IORegister ioregister.hpp "ioregister.hpp"
*/
class IORegister : public Register
{
Q_OBJECT
public:
    /**
    * @brief Constructor, sets the internal data
    *
    * @fn explicit IORegister(int address, int bank, int standard = 0)
    * @param hwInterface
    * @param address
    * @param bank
    * @param standard (Default 0)
    */
    explicit IORegister(HardwareInterface *hwInterface, int address, int bank, int standard = 0);


    /**
    * @brief Destructor
    *
    * @fn ~IORegister()
    */
    ~IORegister();

    /**
    * @brief Set the Value of the allocated Tris Register
    *
    * @fn void setTrisValue(int value)
    * @param value
    */
    void setTrisValue(int value);

    /**
    * @brief Get the Value of the allocated Tris Register
    *
    * @fn int getTrisValue() const
    * @return int
    */
    int getTrisValue() const;

    /**
    * @brief Set a single bit of the allocated Tris Register
    *
    * @fn void setTrisBit(int bit)
    * @param bit
    */
    void setTrisBit(int bit);

    /**
    * @brief Clear a single bit of the allocated Tris Register
    *
    * @fn void clearTrisBit(int bit)
    * @param bit
    */
    void clearTrisBit(int bit);

    /**
    * @brief Check whether the specified bit is set in the allocated Tris Register
    *
    * @fn bool trisIsSet(int bit) const
    * @param bit
    * @return bool
    */
    bool trisIsSet(int bit) const;

    /**
    * @brief Clear the complete Tris Register
    *
    * @fn void clearTris()
    */
    void clearTris();

    /**
    * @brief Save the pointer to a Register as Tris
    *
    * @fn void setTris(Register* trisRegister)
    * @param trisRegister
    */
    void setTris(Register* trisRegister);

    /**
    * @brief Set the Value
    *
    * @fn void setValue(int value, bool force)
    * @param value
    * @param force
    */
    void setValue(int value, bool force);

    /**
    * @brief Returns the Value
    *
    * @fn int getValue() const
    * @param force
    * @return int
    */
    int getValue(bool force = false) const;

    /**
    * @brief Set a single bit
    *
    * @fn void setBit(int bit)
    * @param bit
    */
    void setBit(int bit);

    /**
    * @brief Check whether a specified bit is set
    *
    * @fn bool isSet(int bit) const
    * @param bit
    * @return bool
    */
    bool isSet(int bit) const;

    /**
    * @brief Clear a single bit
    *
    * @fn void clearBit(int bit)
    * @param bit
    */
    void clearBit(int bit);

    /**
    * @brief Clear the complete content
    *
    * @fn void clear()
    */
    void clear();

    /**
    * @brief Set a bit as an input bit
    *
    * @fn void setInput(int bit)
    * @param bit
    */
    void setInput(int bit);

    /**
    * @brief Set a bit as an output bit
    *
    * @fn void setOutput(int bit)
    * @param bit
    */
    void setOutput(int bit);

    /**
    * @brief Convert the allocated Tris register to a String
    *
    * @fn QString trisToString(int bit = -1) const
    * @param bit
    * @return QString
    */
    QString trisToString(int bit = -1) const;

    /**
    * @brief Convert the allocated Latch Register to a String
    *
    * @fn QString latchToString(int bit = -1) const
    * @param bit
    * @return QString
    */
    QString latchToString(int bit = -1) const;

    /**
    * @brief Resets this ioregister to the standard value
    *
    * @fn void reset()
    */
    void reset();

    /**
    * @brief Returns of the given bit of this register is an input or output. \n
    * true if input, false if output
    *
    * @fn bool isInput(int bit) const
    * @param bit
    * @return bool
    */
    bool isInput(int bit) const;

private:
    bool latchIsSet(int bit) const;
    void setLatchValue(int value);
    int getLatchValue() const;
    void setLatchBit(int bit);
    bool getLatchBit(int bit) const;
    void clearLatchBit(int bit);
    void clearLatch();

    Register *tris;
    Register *latch;
    HardwareInterface *hwInterface;

private slots:
    /**
    * @brief Update the output according to the Tris Register
    * @details Save old output pins to Latch if they are converted to input pins
    * Restore Latch values if input pin is converted to output pin
    * This slot is called when the allocated Tris Register has been changed outside
    *
    * @fn void updateOutput(int oldValue)
    * @param oldValue
    */
    void updateOutput(int oldValue);

};

#endif // IOREGISTER_HPP
